package cn.donting.jme.core.input;

import cn.donting.jme.ioc.Bean;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.RawInputListener;
import com.jme3.input.event.*;
import com.jme3.math.Vector2f;

import java.util.HashSet;
import java.util.Objects;


/**
 * @see com.jme3.input
 */
@Bean
public class InputRaw extends AbstractAppState implements RawInputListener {


    private static HashSet<Event> down = new HashSet<>();
    private static HashSet<Event> up = new HashSet<>();
    private static HashSet<Event> press = new HashSet<>();
    private static Vector2f cursorPos = new Vector2f();
    private static Vector2f offsetPos = new Vector2f();
    private static float deltaWheel = 0;

    private static final int MOUSE = 0;
    private static final int KEY = 1;
    private static  InputRaw inputRaw = new InputRaw();

    public InputRaw() {
        inputRaw=this;
    }


    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        app.getInputManager().addRawInputListener(this);
        super.initialize(stateManager, app);
    }

    @Override
    public void beginInput() {
        up.clear();
        down.clear();
        offsetPos.x = 0;
        offsetPos.y = 0;
        deltaWheel = 0;
    }

    @Override
    public void endInput() {
    }

    @Override
    public void onJoyAxisEvent(JoyAxisEvent evt) {

    }

    @Override
    public void onJoyButtonEvent(JoyButtonEvent evt) {

    }

    @Override
    public void onMouseMotionEvent(MouseMotionEvent evt) {
        cursorPos.x = evt.getX();
        cursorPos.y = evt.getY();
        offsetPos.x = evt.getDX();
        offsetPos.y = evt.getDY();
        deltaWheel = evt.getDeltaWheel() / 100f;
    }

    @Override
    public void onMouseButtonEvent(MouseButtonEvent evt) {
        int buttonIndex = evt.getButtonIndex();
        if (evt.isPressed()) {
            down.add(new Event(MOUSE, buttonIndex));
            press.add(new Event(MOUSE, buttonIndex));
        } else {
            up.add(new Event(MOUSE, buttonIndex));
            press.remove(new Event(MOUSE, buttonIndex));
        }
    }

    @Override
    public void onKeyEvent(KeyInputEvent evt) {
        if (evt.getKeyCode() == KeyInput.KEY_UNKNOWN) {
            return;
        }
        int keyCode = evt.getKeyCode();
        if (evt.isPressed()) {
            down.add(new Event(KEY, keyCode));
            press.add(new Event(KEY, keyCode));
        } else {
            up.add(new Event(KEY, keyCode));
            if(!evt.isRepeating()){
                press.remove(new Event(KEY, keyCode));
            }
        }

    }

    @Override
    public void onTouchEvent(TouchEvent evt) {

    }

    public static Vector2f cursorPos() {
        return cursorPos.clone();
    }

    public static Vector2f offsetPos() {
        return offsetPos.clone();
    }

    public static float deltaWheel() {
        return deltaWheel;
    }

    public static boolean keyDown(int key) {
        return down.contains(new Event(KEY, key));
    }

    public static boolean keyUp(int key) {
        return up.contains(new Event(KEY, key));
    }

    public static boolean keyPress(int key) {
        return press.contains(new Event(KEY, key));
    }

    public static boolean mouseButtonDown(int btn) {
        return down.contains(new Event(MOUSE, btn));
    }

    /**
     *
     * @param btn
     * @see MouseInput
     *
     * @return
     */
    public static boolean mouseButtonUp(int btn) {
        return up.contains(new Event(MOUSE, btn));
    }
    public static boolean mousePress(int btn) {
        return press.contains(new Event(MOUSE, btn));
    }

    private static class Event {
        /**
         * 0 mouse
         * 1 key
         */
        public final int type;
        public final int code;

        public Event(int type, int code) {
            this.type = type;
            this.code = code;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Event event = (Event) o;
            return type == event.type && code == event.code;
        }

        @Override
        public int hashCode() {
            return Objects.hash(type, code);
        }
    }
}
